﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace Universe.SimpleQueue
{
    public class QueueStatistic
    {
        private Counters _total = new Counters();
        private Dictionary<string, Counters> _byQueue = new Dictionary<string, Counters>();
        private Object _sync = new Object();

        public class Counters
        {
            public long Publish;
            public long Postpone;
            public long Ack;
            public long InProcess;

            public Counters()
            {
            }

            private Counters(long publish, long postpone, long ack, long inProcess)
            {
                Publish = publish;
                Postpone = postpone;
                Ack = ack;
                InProcess = inProcess;
            }

            public Counters Clone()
            {
                return new Counters(Publish, Postpone, Ack, InProcess);
            }
        }


        public Counters Total
        {
            get { lock (_sync) return _total.Clone(); }
        }

        public Counters ByQueue(string queueName)
        {
            lock(_sync)
            {
                
                Counters ret;
                _byQueue.TryGetValue(queueName, out ret);
                return ret ?? new Counters();
            }
        }

        public void Clean()
        {
            lock (_sync)
            {
                this._total = new Counters();
                this._byQueue.Clear();
            }
        }

        public QueueStatistic Clone()
        {
            lock (_sync)
            {
                QueueStatistic ret = new QueueStatistic();
                ret._total = _total.Clone();
                foreach (var pairs in _byQueue)
                    ret._byQueue[pairs.Key] = pairs.Value.Clone();

                return ret;
            }
        }

        internal void IncAck(string queueName)
        {
            lock (_sync)
            {
                _total.Ack++;
                GetOrCreate(queueName).Ack++;
            }
        }

        internal void IncPublish(string queueName)
        {
            lock (_sync)
            {
                _total.Publish++;
                GetOrCreate(queueName).Publish++;
            }
        }

        internal void IncPostpone(string queueName)
        {
            lock (_sync)
            {
                _total.Postpone++;
                GetOrCreate(queueName).Postpone++;
            }
        }

        internal void IncInProcess(string queueName, int delta)
        {
            lock (_sync)
            {
                _total.InProcess += delta;
                var c = GetOrCreate(queueName);
                c.InProcess += delta;
            }
        }

        private Counters GetOrCreate(string queueName)
        {
            Counters value;
            if (!_byQueue.TryGetValue(queueName, out value))
                _byQueue[queueName] = value = new Counters();

            return value;
        }

        public String AsConsoleTable()
        {
            var copy = this.Clone();

            var full = copy.Clone()._byQueue;

            var l1 = copy._byQueue.Values;

            var x = copy.Total;
            var max = new[] {x.Ack, x.InProcess, x.Postpone, x.Publish}.Max();
            const string fmt = "###,###,##0";
            var maxl = max.ToString().Length;

            StringBuilder ret = new StringBuilder();

            Action<object[]> append0 = (args) =>
            {
                ret.AppendFormat("{0," + maxl + "} | {1," + maxl + "} | {2," + maxl + "} | {3," + maxl + "} | {4}",
                    args).AppendLine();
            };

            Action<Counters, string> append = (counters, str) => append0(new object[]
            {
                counters.Publish,
                counters.Ack,
                counters.Postpone,
                counters.InProcess,
                str
            });

            string sep = new string('-', maxl);
            append0(new object[] { "Pub", "Ack", "Psp", "Inp", "" });
            append0(new object[] { sep, sep, sep, sep, "-----------------"});

            foreach (var p in copy._byQueue.OrderBy(y => y.Key))
            {
                string name = p.Key;
                var c = p.Value;
                append(c, name);
            }

            append0(new object[] { sep, sep, sep, sep, "-----------------" });
            append(copy.Total, "TOTAL");

            return ret.ToString();
        }

    }
}